﻿
using Furion.DatabaseAccessor;
using Furion.DataEncryption;
using Furion.DynamicApiController;
using Furion.FriendlyException;
using Furion.Core;
using Mapster;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using Furion.DatabaseAccessor.Extensions;
using Furion.DistributedIDGenerator;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Threading.Tasks;
using Furion.Core.Entities.Enums;
using Furion.RemoteRequest.Extensions;
using System.Security.Cryptography;
using System.Text;
using Microsoft.Extensions.Caching.Memory;
using StackExchange.Profiling.Internal;
using Microsoft.IdentityModel.JsonWebTokens;
using System.Reflection;

namespace Furion.Application
{

    /// <summary>
    /// 授权
    /// </summary>
    [AppAuthorize, ApiDescriptionSettings("System", Name = "Auth", Order = 100, Tag = "授权")]
    public class UserAuthService : IDynamicApiController, IUserAuthService, ITransient
    {
        private readonly IRepository<User> _userRepository;
        private readonly IRepository<UserRole> _userRoleRepository;

        private readonly IRepository<Permission> _permissionRepository;

        private readonly IRepository<RolePermission> _rolePermissionRepository;

        private readonly IHttpContextAccessor _httpContextAccessor;
        private readonly IMemoryCache _memoryCache;
        public UserAuthService(
            IHttpContextAccessor httpContextAccessor
            , IRepository<User> userRepository
            , IRepository<Permission> permissionRepository
            , IRepository<UserRole> userRoleRepository
            , IRepository<RolePermission> rolePermissionRepository
            , IMemoryCache memoryCache

            )
        {
            _httpContextAccessor = httpContextAccessor;
            _userRepository = userRepository;
            _userRoleRepository = userRoleRepository;

            _permissionRepository = permissionRepository;
            _rolePermissionRepository = rolePermissionRepository;
            _memoryCache = memoryCache;
        }


        [AllowAnonymous,HttpGet("/Auth/Test"), NonUnify]
        public string Test()
        {
            return $"{Assembly.GetExecutingAssembly().FullName};IP:{HttpContextExtensions.GetRemoteIpAddressToIPv4(_httpContextAccessor.HttpContext)};当前时间：{DateTime.Now:G};Port：{App.Configuration["ConsulRegisterOptions:Port"]}";
        }



        [AllowAnonymous,HttpPost("/Auth/Token"), ApiDescriptionSettings(Name = "Token")]
        public JsonWebToken ShowToken([FromForm]string token)
        {
             var jwt =  JWTEncryption.ReadJwtToken(token);

             return   jwt;

        }


        /// <summary>
        /// 登录授权
        /// </summary>
        /// <param name="input"></param>
        /// <remarks></remarks>
        /// <returns></returns>
        [AllowAnonymous, IfException(1000, ErrorMessage = "用户名或密码错误"), HttpPost("/Auth/Login"), ApiDescriptionSettings(Name = "Login")]
        public async Task<LoginOutput> Login(LoginInput input)
        { 
            var EncryptPassword = MD5Encryption.Encrypt(input.UserPass.Trim());

            var user_info = await _userRepository.Include(a=>a.Roles).FirstOrDefaultAsync(a => a.UserName.ToLower() == input.UserName.Trim() && a.UserPass == EncryptPassword && a.IsDeleted==false);
            if(user_info==null)
            {
                throw Oops.Oh("账号或密码错误，登录失败");
            }
            else
            {
                if(!user_info.Enabled)
                {
                    throw Oops.Oh("您的帐号已禁用");
                }

                //if (!string.IsNullOrEmpty(input.openid))
                //{
                //    if(user_info.OpenId != input.openid)
                //    {
                //        user_info.OpenId = input.openid;
                //    }
                //}

                user_info.LoginTime = DateTime.Now;
                await _userRepository.UpdateNowAsync(user_info);
                
                 

                var output = user_info.Adapt<LoginOutput>();

                var Ids = user_info.Roles.Select(a => a.Id).ToList();

                output.Roles = user_info.Roles.Adapt<ICollection<RoleOutput>>();

                if(Ids.Count>0)
                {
                    var permissionIds =await _rolePermissionRepository.DetachedEntities.Where(a => Ids.Contains(a.RoleId)).Select(a => a.PermissionId).Distinct().ToListAsync();
                    if(permissionIds.Count>0)
                    {
                        output.Permissions = _permissionRepository.DetachedEntities.Where(a => permissionIds.Contains(a.Id)).ToList().Adapt<List<PermissionOutInput>>();
                    }
                }
                


                var accessToken = JWTEncryption.Encrypt(new Dictionary<string, object>()
                {
                    {ClaimConst.CLAINM_USERID,user_info.Id },
                    {ClaimConst.CLAINM_ACCOUNT,user_info.UserName },
                    {ClaimConst.CLAINM_NAME,user_info.NickName }
                });

                output.AccessToken = accessToken;



                // 获取刷新 token
                var refreshToken = JWTEncryption.GenerateRefreshToken(accessToken, 43200); // 第二个参数是刷新 token 的有效期（分钟），默认三十天

                // 设置响应报文头
                _httpContextAccessor.HttpContext.Response.Headers["access-token"] = accessToken;
                _httpContextAccessor.HttpContext.Response.Headers["x-access-token"] = refreshToken;                 
                _httpContextAccessor.HttpContext.SigninToSwagger(output.AccessToken);

                return output;
            }
        }


        /// <summary>
        /// 获取OpenId
        /// </summary>
        /// <param name="code"></param>
        /// <returns></returns>
        [AllowAnonymous, HttpGet("/Auth/GetOpenId")]
        public async Task<Jscode2sessionResponse> GetOpenId(string code)
        {
            string WxOpenAppID = App.Configuration["AppInfo:WxOpenAppID"];
            string WxOpenAppSecret = App.Configuration["AppInfo:WxOpenAppSecret"];
            string HostUrl = "https://api.weixin.qq.com/sns/jscode2session?appid="+ WxOpenAppID + "&secret="+ WxOpenAppSecret + "&js_code="+ code + "&grant_type=authorization_code";
            var rs = await  HostUrl.GetAsAsync<Jscode2sessionResponse>();
            return rs;
        }


        /// <summary>
        /// 微信一键登录 （登录注册二合一）
        /// </summary>
        /// <param name="para"></param>
        /// <returns></returns>
        [AllowAnonymous,HttpPost("/Auth/GetUserPhoneNumber")]
        public async Task<dynamic> GetUserPhoneNumber(WxPara para)
        {
            string WxOpenAccessTokenHost = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={0}&secret={1}";
            string WxOpenAppID = App.Configuration["AppInfo:WxOpenAppID"];
            string WxOpenAppSecret = App.Configuration["AppInfo:WxOpenAppSecret"];
            string HostUrl = string.Format(WxOpenAccessTokenHost, WxOpenAppID, WxOpenAppSecret);
            var token = _memoryCache.Get<WxOpenAccessToken>(ClaimConst.WeixinAccessToken);
            if(token==null)
            {
                token= await HostUrl.GetAsAsync<WxOpenAccessToken>();
                _memoryCache.Set(ClaimConst.WeixinAccessToken, token,DateTime.Now.AddSeconds(7200));
            }
            if (token.errcode==0)
            {
                string PhoneHost = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token="+ token.access_token;
                var WxUserPhone = await PhoneHost.SetBody(new { code = para.code }, "application/json").PostAsAsync<WxUserPhone>();

                var isExist = await _userRepository.AnyAsync(u => u.UserName.ToLower() == WxUserPhone.phone_info.phoneNumber);
                if (isExist)
                {                    
                    var user_info = await _userRepository.FirstOrDefaultAsync(u => u.UserName.ToLower() == WxUserPhone.phone_info.phoneNumber);


                    if (user_info.IsDeleted)
                    {
                        throw Oops.Oh("帐号已删除");
                    }

                    if (!user_info.Enabled)
                    {
                        throw Oops.Oh("您的帐号已禁用");
                    }


                    if (!string.IsNullOrEmpty(para.openid))
                    {
                        if(para.openid!=user_info.OpenId)
                        {
                            user_info.OpenId = para.openid;                            
                        }                        
                    }
                    user_info.LoginTime = DateTime.Now;
                    await _userRepository.UpdateNowAsync(user_info);
                    

                    var output = user_info.Adapt<LoginOutput>();

                    output.Roles = user_info.Roles.Adapt<ICollection<RoleOutput>>();

                    var accessToken = JWTEncryption.Encrypt(new Dictionary<string, object>()
                    {
                        {ClaimConst.CLAINM_USERID,user_info.Id },
                        {ClaimConst.CLAINM_ACCOUNT,user_info.UserName },
                        {ClaimConst.CLAINM_NAME,user_info.NickName }
                    });
                    output.AccessToken = accessToken;

                    // 获取刷新 token
                    var refreshToken = JWTEncryption.GenerateRefreshToken(accessToken, 43200); // 第二个参数是刷新 token 的有效期（分钟），默认三十天

                    // 设置响应报文头
                    _httpContextAccessor.HttpContext.Response.Headers["access-token"] = accessToken;
                    _httpContextAccessor.HttpContext.Response.Headers["x-access-token"] = refreshToken;

                    //_httpContextAccessor.HttpContext.Response.Headers["user-role"] = JsonSerialization.JSON.Serialize(user.Roles) ;

                    _httpContextAccessor.HttpContext.SigninToSwagger(output.AccessToken);
                    return output;
                }
                else
                {
                    var UserPass = MD5Encryption.Encrypt(WxUserPhone.phone_info.phoneNumber.Substring(5, 6));
                    var addInput = new User
                    {
                        OpenId=para.openid,
                        CreatedUserId = 0,
                        Enabled = true,
                        ImageUrl = "",
                        IsDeleted = false,                        
                        NickName = "",
                        OrgId = 0,
                        Remark = "",                        
                        UserName = WxUserPhone.phone_info.phoneNumber,
                        UserPass = UserPass,
                        CreatedTime = DateTime.Now,
                        LoginTime = DateTime.Now,
                        UpdatedTime = DateTime.Now
                    };
                    var newEntity = await _userRepository.InsertNowAsync(addInput);

                    //设置小程序用户角色
                    string DefaultAppRoleId = App.Configuration["AppInfo:DefaultAppRoleId"].ToString();
                    var userRole = new UserRole { RoleId=Convert.ToInt32(DefaultAppRoleId), UserId= newEntity.Entity.Id };
                    await _userRoleRepository.InsertNowAsync(userRole);

                    var output = newEntity.Entity.Adapt<LoginOutput>();
                    output.Roles = null;
                    var accessToken = JWTEncryption.Encrypt(new Dictionary<string, object>()
                    {
                        {ClaimConst.CLAINM_USERID,newEntity.Entity.Id },
                        {ClaimConst.CLAINM_ACCOUNT,newEntity.Entity.UserName },
                        {ClaimConst.CLAINM_NAME,newEntity.Entity.NickName }
                    });

                    output.AccessToken = accessToken;

                    // 获取刷新 token
                    var refreshToken = JWTEncryption.GenerateRefreshToken(accessToken, 43200); // 第二个参数是刷新 token 的有效期（分钟），默认三十天

                    // 设置响应报文头
                    _httpContextAccessor.HttpContext.Response.Headers["access-token"] = accessToken;
                    _httpContextAccessor.HttpContext.Response.Headers["x-access-token"] = refreshToken;

                    //_httpContextAccessor.HttpContext.Response.Headers["user-role"] = JsonSerialization.JSON.Serialize(user.Roles) ;

                    _httpContextAccessor.HttpContext.SigninToSwagger(output.AccessToken);
                    return output;
                }
                    
            }
            else
            {
                throw Oops.Oh(token.errmsg);
            }


        }

        /// <summary>
        /// 获取手机号 （小程序）
        /// </summary>
        /// <param name="code"></param>
        /// <returns></returns>
        [AllowAnonymous, HttpGet("/Auth/GetPhoneNumber")]
        public async Task<WxUserPhone> GetPhoneNumber(string code)
        {
            string WxOpenAccessTokenHost = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={0}&secret={1}";
            string WxOpenAppID = App.Configuration["AppInfo:WxOpenAppID"];
            string WxOpenAppSecret = App.Configuration["AppInfo:WxOpenAppSecret"];
            string HostUrl = string.Format(WxOpenAccessTokenHost, WxOpenAppID, WxOpenAppSecret);
            var token = _memoryCache.Get<WxOpenAccessToken>(ClaimConst.WeixinAccessToken);
            if (token == null)
            {
                token = await HostUrl.GetAsAsync<WxOpenAccessToken>();
                _memoryCache.Set(ClaimConst.WeixinAccessToken, token, DateTime.Now.AddSeconds(7200));
            }
            if (token.errcode == 0)
            {
                string PhoneHost = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=" + token.access_token;
                var WxUserPhone = await PhoneHost.SetBody(new { code = code }, "application/json").PostAsAsync<WxUserPhone>();

                return WxUserPhone;
            }
            else
            {
                throw Oops.Oh(token.errmsg);
            }
        }

        /// <summary>
        /// 注册 （小程序）
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [AllowAnonymous, HttpPost("/Auth/Register")]
        public async Task<WeiXinLoginOutput> Register(RegisterInput input)
        {
            var val = await _userRepository.DetachedEntities.AnyAsync(a => a.UserName == input.UserName&& a.IsDeleted==false);
            if(val)
            {
                throw Oops.Oh("请不要重复注册");
            }
            else
            {
                string Password = input.UserName.Substring(5,6);
                var EncryptPassword = MD5Encryption.Encrypt(Password);

                var add = input.Adapt<User>();
                add.UserPass = EncryptPassword;
                add.NickName = "";
                add.ImageUrl = "";
                add.Remark = "";
                add.OrgId = 0;
                add.LoginTime = DateTime.Now;
                add.Enabled = true;
                add.CreatedTime = DateTime.Now;
                add.UpdatedTime = DateTime.Now;
                add.CreatedUserId = 0;
                add.CreatedUserName = "";
                add.UpdatedUserId = 0;
                add.UpdatedUserName = "";
                add.IsDeleted = false;

                var newEntity =await _userRepository.InsertNowAsync(add);

                var output = newEntity.Entity.Adapt<WeiXinLoginOutput>();                
                var accessToken = JWTEncryption.Encrypt(new Dictionary<string, object>()
                    {
                        {ClaimConst.CLAINM_USERID,newEntity.Entity.Id },
                        {ClaimConst.CLAINM_ACCOUNT,newEntity.Entity.UserName },
                        {ClaimConst.CLAINM_NAME,newEntity.Entity.NickName }
                    });

                output.AccessToken = accessToken;

                // 获取刷新 token
                var refreshToken = JWTEncryption.GenerateRefreshToken(accessToken, 43200); // 第二个参数是刷新 token 的有效期（分钟），默认三十天

                // 设置响应报文头
                _httpContextAccessor.HttpContext.Response.Headers["access-token"] = accessToken;
                _httpContextAccessor.HttpContext.Response.Headers["x-access-token"] = refreshToken;
                //_httpContextAccessor.HttpContext.Response.Headers["user-role"] = JsonSerialization.JSON.Serialize(user.Roles) ;

                _httpContextAccessor.HttpContext.SigninToSwagger(output.AccessToken);
                return output;
            }
        }


        /// <summary>
        /// 一键登录（小程序）
        /// </summary>
        /// <param name="para"></param>
        /// <returns></returns>
        [AllowAnonymous, HttpPost("/Auth/WeiXinLogin")]
        public async Task<WeiXinLoginOutput> WeiXinLogin(WxPara para)
        {
            string WxOpenAccessTokenHost = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={0}&secret={1}";
            string WxOpenAppID = App.Configuration["AppInfo:WxOpenAppID"];
            string WxOpenAppSecret = App.Configuration["AppInfo:WxOpenAppSecret"];
            string HostUrl = string.Format(WxOpenAccessTokenHost, WxOpenAppID, WxOpenAppSecret);
            var token = _memoryCache.Get<WxOpenAccessToken>(ClaimConst.WeixinAccessToken);
            if (token == null)
            {
                token = await HostUrl.GetAsAsync<WxOpenAccessToken>();
                _memoryCache.Set(ClaimConst.WeixinAccessToken, token, DateTime.Now.AddSeconds(7200));
            }
            if (token.errcode == 0)
            {
                string PhoneHost = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=" + token.access_token;
                var WxUserPhone = await PhoneHost.SetBody(new { code = para.code }, "application/json").PostAsAsync<WxUserPhone>();

                var isExist = await _userRepository.AnyAsync(u => u.UserName.ToLower() == WxUserPhone.phone_info.phoneNumber);
                if (isExist)
                {
                    var user_info = await _userRepository.DetachedEntities.FirstOrDefaultAsync(u => u.UserName.ToLower() == WxUserPhone.phone_info.phoneNumber);
                    if (user_info.IsDeleted)
                    {
                        throw Oops.Oh("您的帐号已禁用！");
                    }
                    if (!user_info.Enabled)
                    {
                        throw Oops.Oh("您的帐号已禁用");
                    }

                    var output = user_info.Adapt<WeiXinLoginOutput>();
                    var accessToken = JWTEncryption.Encrypt(new Dictionary<string, object>()
                    {
                        {ClaimConst.CLAINM_USERID,user_info.Id },
                        {ClaimConst.CLAINM_ACCOUNT,user_info.UserName },
                        {ClaimConst.CLAINM_NAME,user_info.NickName }
                    });
                    output.AccessToken = accessToken;

                    // 获取刷新 token
                    var refreshToken = JWTEncryption.GenerateRefreshToken(accessToken, 43200); // 第二个参数是刷新 token 的有效期（分钟），默认三十天

                    // 设置响应报文头
                    _httpContextAccessor.HttpContext.Response.Headers["access-token"] = accessToken;
                    _httpContextAccessor.HttpContext.Response.Headers["x-access-token"] = refreshToken;

                    //_httpContextAccessor.HttpContext.Response.Headers["user-role"] = JsonSerialization.JSON.Serialize(user.Roles) ;

                    _httpContextAccessor.HttpContext.SigninToSwagger(output.AccessToken);
                    return output;
                }
                else
                {
                    throw Oops.Oh("请先注册");
                }
            }
            else
            {
                throw Oops.Oh(token.errmsg);
            }
        }


        /// <summary>
        /// 自动登录（小程序）
        /// </summary>
        /// <param name="OpenId"></param>
        /// <returns></returns>
        [AllowAnonymous, HttpGet("/Auth/AutoLogin")]
        public async Task<WeiXinLoginOutput> AutoLogin([Required]string OpenId)
        {
            var isExist = await _userRepository.AnyAsync(u => u.OpenId == OpenId);
            if (isExist)
            {
                var user_info = await _userRepository.DetachedEntities.FirstOrDefaultAsync(u => u.OpenId == OpenId);
                if (user_info.IsDeleted)
                {
                    throw Oops.Oh("您的帐号已禁用！");
                }
                if (!user_info.Enabled)
                {
                    throw Oops.Oh("您的帐号已禁用");
                }

                var output = user_info.Adapt<WeiXinLoginOutput>();
                var accessToken = JWTEncryption.Encrypt(new Dictionary<string, object>()
                    {
                        {ClaimConst.CLAINM_USERID,user_info.Id },
                        {ClaimConst.CLAINM_ACCOUNT,user_info.UserName },
                        {ClaimConst.CLAINM_NAME,user_info.NickName }
                    });
                output.AccessToken = accessToken;

                // 获取刷新 token
                var refreshToken = JWTEncryption.GenerateRefreshToken(accessToken, 43200); // 第二个参数是刷新 token 的有效期（分钟），默认三十天

                // 设置响应报文头
                _httpContextAccessor.HttpContext.Response.Headers["access-token"] = accessToken;
                _httpContextAccessor.HttpContext.Response.Headers["x-access-token"] = refreshToken;

                //_httpContextAccessor.HttpContext.Response.Headers["user-role"] = JsonSerialization.JSON.Serialize(user.Roles) ;

                _httpContextAccessor.HttpContext.SigninToSwagger(output.AccessToken);
                return output;
            }
            else
            {
                throw Oops.Oh("请先注册");
            }
        }



        /// <summary>
        /// 网关验证登录
        /// </summary>
        /// <param name="input"></param>
        /// <remarks></remarks>
        /// <returns></returns>
        [AllowAnonymous, IfException(1000, ErrorMessage = "用户名或密码错误"), HttpPost("/Auth/ValidateLogin"), ApiDescriptionSettings(Name = "ValidateLogin")]
        public async Task<ValidateOutput> ValidateLogin(ValidateInput input)
        {
            var EncryptPassword = MD5Encryption.Encrypt(input.UserPass.Trim());

            var user_info = await _userRepository.Include(a => a.Roles).FirstOrDefaultAsync(a => a.UserName.ToLower() == input.UserName.Trim() && a.UserPass == EncryptPassword && a.IsDeleted == false);
            if (user_info == null)
            {
                throw Oops.Oh("账号或密码错误，登录失败");
            }
            else
            {
                if (!user_info.Enabled)
                {
                    throw Oops.Oh("您的帐号已禁用");
                }

                user_info.LoginTime = DateTime.Now;
                await _userRepository.UpdateNowAsync(user_info);

                var output = user_info.Adapt<ValidateOutput>();

                var Ids = user_info.Roles.Select(a => a.Id).ToList();

                output.Roles = user_info.Roles.Adapt<ICollection<RoleOutput>>();

                if (Ids.Count > 0)
                {
                    var permissionIds = await _rolePermissionRepository.DetachedEntities.Where(a => Ids.Contains(a.RoleId)).Select(a => a.PermissionId).Distinct().ToListAsync();
                    if (permissionIds.Count > 0)
                    {
                        output.Permissions = _permissionRepository.DetachedEntities.Where(a => permissionIds.Contains(a.Id)).ToList().Adapt<List<PermissionOutInput>>();
                    }
                } 
                 
                return output;
            }
        }

        /// <summary>
        /// 检测用户是否存在
        /// </summary>
        /// <param name="Id"></param>
        /// <remarks></remarks>
        /// <returns></returns>
        [AllowAnonymous, IfException(1000, ErrorMessage = "用户不存在"), HttpPost("/Auth/ValidateExists"), ApiDescriptionSettings(Name = "ValidateExists")]
        public async Task<bool> ValidateExists(long Id)
        {
            var val = await _userRepository.AnyAsync(a => a.Id==Id);             
            return val;            
        }


    }
}
